#include "myhead.h"

// max number of consecutive failed attempts
int MAX_ERR_COUNT = 0;
// server IP
char serverIP[] = "127.0.0.1";

// record clients who try to connect to server


// ClientInfo class
// manager client infomation
typedef struct _ClientInfo {
	struct sockaddr_in m_IP;
	int m_loginTimestamp;
	int m_preLoginTime;
	char m_username[MAXX];
	int m_UDPServerPort;
	int m_errorCount;
} ClientInfo;
ClientInfo client_connect_list[MAXX];
int num_client_connect = -1;

void append_client_connect_list(ClientInfo client) {
	client_connect_list[++num_client_connect] = client;
}

void start(int port);

void ClientInfo_Constructor(ClientInfo *client, struct sockaddr_in IP) {
	memset(&client->m_IP, 0, sizeof(client->m_IP));
	memcpy(&client->m_IP, &IP, sizeof(struct sockaddr_in));
	client->m_loginTimestamp = -1;
	client->m_preLoginTime = -1;
	memset(client->m_username, 0, sizeof(client->m_username));
	client->m_UDPServerPort = -1;
	client->m_errorCount = 0;
}

void ClientInfo_init(ClientInfo *client) {
	memset(&client->m_IP, 0, sizeof(client->m_IP));
	client->m_loginTimestamp = -1;
	client->m_preLoginTime = -1;
	memset(client->m_username, 0, sizeof(client->m_username));
	client->m_UDPServerPort = -1;
	client->m_errorCount = 0;
}

bool is_sockaddr_in_equal(struct sockaddr_in a, struct sockaddr_in b);

bool ClientInfo_add_logInfo(ClientInfo *client, char name[], int timestamp, int UDPport) {
	strcpy(client->m_username, name);
	client->m_loginTimestamp = timestamp;
	client->m_UDPServerPort = UDPport;
	for (int i = 0; i < num_client_connect; ++i) {
		if (is_sockaddr_in_equal(client_connect_list[i].m_IP, client->m_IP)) {
			client_connect_list[i] = *client;
		}
	}
	if (client->m_errorCount < MAX_ERR_COUNT) {
		client->m_preLoginTime = client->m_loginTimestamp;
		if (__DEBUG)
			printf("Debug: self.m_errorCount: %d\n", client->m_errorCount);
		return True;
	} else {
		if ((int) client->m_loginTimestamp - (int) client->m_preLoginTime > 30) {
			client->m_errorCount = 0;
			client->m_errorCount++;
			return True;
		} else {
			return False;
		}
	}
	
	
}

// record number_of_consecutive_failed_attempts
void ClientInfo_error_password(ClientInfo *client) {
	client->m_errorCount++;
}

bool is_sockaddr_in_equal(struct sockaddr_in a, struct sockaddr_in b) {
	if (a.sin_port == b.sin_port &&
	    a.sin_addr.s_addr == b.sin_addr.s_addr) {
		return True;
	} else {
		return False;
	}
}

bool check_credential(char username[], char password[]) {
	bool res = False;
	FILE *file = fopen("credentials.txt", "r");
	char credential[MAXX];
	while (fgets(credential, MAXX, file) != NULL) {
		char name[MAXX];
		char pwd[MAXX];
		strip(credential);
		String credential_split[10] = {0};
		int num_credential_split;
		split(credential, " ", credential_split, &num_credential_split);
		strcpy(name, credential_split[0]);
		strcpy(pwd, credential_split[1]);
		if (strcmp(name, username) == 0 && strcmp(pwd, password) == 0) {
			res = True;
			break;
		}
	}
	if (__DEBUG)
		printf("check_credential: res: %d\n", res);
	return res;
}

ClientInfo get_client_from_connect_list(struct sockaddr_in IP, bool *flag) {
	for (int i = 0; i < num_client_connect; ++i) {
		if (is_sockaddr_in_equal(client_connect_list[i].m_IP, IP)) {
			*flag = True;
			return client_connect_list[i];
		}
	}
	*flag = False;
	ClientInfo empty_client;
	return empty_client;
}

void add_user_log(char name[1000], int timestamp, struct sockaddr_in IP, int UDPport) {
	int dt = timestamp;
	char address[20]; //存放点分十进制IP地址
	inet_ntop(AF_INET, (void *) &IP, address, 16);
	
	char txt[MAXX];
	memset(txt, 0, sizeof(txt));
	sprintf(txt, "%d; %s; %s; %d", dt, name, address, UDPport);
	int index = 0;
	// read old file to get log index
	FILE *user_file = fopen("cse_userlog.txt", "r");
	char last_line[MAXX];
	memset(last_line, 0, sizeof(last_line));
	while (fgets(last_line, MAXX, user_file) != NULL) {
		//do nothing
	}
	if (strlen(last_line) > 0) {
		strip(last_line);
		String last_line_split[MAXX];
		int num_last_line_split;
		split(last_line, "; ", last_line_split, &num_last_line_split);
		index = atoi(last_line_split[0]);
	} else {
		index = 0;
	}
	fclose(user_file);
	// write new log in file
	user_file = fopen("cse_userlog.txt", "a+");
	char msg[MAXX];
	sprintf(msg, "%d; %s", index + 1, txt);
	fprintf(user_file, "%s\n", msg);
	fclose(user_file);
}

int add_msg_log(char name[], int timestamp, char txt[]) {
	int dt = timestamp;
	char log_txt[MAXX];
	sprintf(log_txt, "%d; %s; %s", dt, name, txt);
	int index = 0;
	FILE *msg_file = fopen("messagelog.txt", "r");
	char last_line[MAXX];
	memset(last_line, 0, sizeof(last_line));
	while (fgets(last_line, MAXX, msg_file) != NULL) {
		//do nothing
	}
	if (strlen(last_line) > 0) {
		strip(last_line);
		String last_line_split[MAXX];
		int num_last_line_split;
		split(last_line, "; ", last_line_split, &num_last_line_split);
		index = atoi(last_line_split[0]);
	} else {
		index = 0;
	}
	fclose(msg_file);
	msg_file = fopen("messagelog.txt", "a+");
	char msg[MAXX];
	sprintf(msg, "%d; %s; no", index + 1, log_txt);
	fprintf(msg_file, "%s\n", msg);
//	fprintf(msg_file, "Hello World!\n");
//	fputs("Fuck you",msg_file);
	fclose(msg_file);
	return index + 1;
}

int dt_to_timestamp(char dt[]) {
	return atoi(dt);
}

char new_log_list[MAXX][MAXX];
int num_new_log_list = -1;

void append_new_log_list(char log[]) {
	strcpy(new_log_list[++num_new_log_list], log);
}

char **read_msg_log(int timestamp) {
	FILE *msg_file = fopen("messagelog.txt", "r");
	char log[MAXX];
	while (fgets(log, MAXX, msg_file)) {
		String log_arr[MAXX];
		int num_log_arr = 0;
		split(log, "; ", log_arr, &num_log_arr);
		char log_dt[MAXX];
		strcpy(log_dt, log_arr[1]);
		int log_timestamp = dt_to_timestamp(log_dt);
		char log_name[MAXX];
		strcpy(log_name, log_arr[2]);
		if (log_timestamp < timestamp) {
			//pass
		} else {
			append_new_log_list(log);
		}
	}
	fclose(msg_file);
	return new_log_list;
}

char new_log_list_for_edit_msg_log[MAXX][MAXX];
int num_log_list_for_edit_msg_log = -1;

void append_new_log_list_for_edit_msg_log(char log[]) {
	strcpy(new_log_list_for_edit_msg_log[++num_log_list_for_edit_msg_log], log);
}

void writelines_new_log_list_for_edit_msg_log(FILE *pFile) {
	for (int i = 0; i < num_log_list_for_edit_msg_log; ++i) {
		fputs(new_log_list_for_edit_msg_log[i], pFile);
	}
}

typedef struct _edit_res_edit_time {
	bool edit_res;
	char edit_time[MAXX];
} edit_res_edit_time;

edit_res_edit_time edit_msg_log(char name[], int edit_index, char edit_dt[], char edit_txt[]) {
	bool edit_res = False;
	char edit_time[MAXX];
	memset(edit_time, 0, sizeof(edit_time));
	FILE *msg_file = fopen("messagelog.txt", "r");
	char log[MAXX];
	while (fgets(log, MAXX, msg_file) != NULL) {
		String log_arr[MAXX];
		int num_log_arr = 0;
		split(log, "; ", log_arr, &num_log_arr);
		char log_index[MAXX];
		strcpy(log_index, log_arr[0]);
		char log_txt[MAXX];
		strcpy(log_txt, log_arr[3]);
		char log_dt[MAXX];
		strcpy(log_dt, log_arr[1]);
		char log_name[MAXX];
		strcpy(log_name, log_arr[2]);
		if (strcmp(log_dt, edit_dt) == 0 &&
		    strcmp(log_name, name) == 0 &&
		    atoi(log_index) == edit_index) {
			edit_res = True;
			sprintf(edit_time, "%d", time(0));
			strcpy(log_arr[3], edit_txt);
			strcpy(log_arr[1], edit_time);
			strcpy(log_arr[4], "yes\n");
			if (__DEBUG) {
				printf("Debug: find same name and index and time need to edit: %s\n", log_arr[0]);
			}
			sprintf(log, "%s; %s; %s; %s; %s", log_arr[0], log_arr[1], log_arr[2], log_arr[3], log_arr[4]);
			append_new_log_list_for_edit_msg_log(log);
			if (__DEBUG) {
				printf("Debug: new log %s\n", log);
			}
		} else {
			append_new_log_list_for_edit_msg_log(log);
		}
	}
	fclose(msg_file);
	msg_file = fopen("messagelog.txt", "w");
	writelines_new_log_list_for_edit_msg_log(msg_file);
	fclose(msg_file);
	edit_res_edit_time tmp;
	tmp.edit_res = edit_res;
	strcpy(tmp.edit_time, edit_time);
	return tmp;
}

char new_log_list_for_del_msg_log[MAXX][MAXX];
int num_new_log_list_for_del_msg_log = -1;

void append_new_log_list_for_del_msg_log(char msg[]) {
	strcpy(new_log_list_for_del_msg_log[++num_new_log_list_for_del_msg_log], msg);
}

void writelines_new_log_list_for_del_msg_log(FILE *pFile) {
	for (int i = 0; i < num_new_log_list_for_del_msg_log; ++i) {
		fputs(new_log_list_for_del_msg_log[i], pFile);
	}
	
}

typedef struct _del_res_del_txt {
	bool del_res;
	char del_txt[MAXX];
} del_res_del_txt;

del_res_del_txt del_msg_log(char name[1000], int del_index, char del_dt[1000]) {
	bool del_res = False;
	char del_txt[MAXX];
	memset(del_txt, 0, sizeof(del_txt));
	int index = 1;
	FILE *msg_file = fopen("messagelog.txt", "r");
	char log[MAXX];
	while (fgets(log, MAXX, msg_file) != NULL) {
		String log_arr[MAXX];
		int num_log_arr = 0;
		split(log, "; ", log_arr, &num_log_arr);
		char log_txt[MAXX][MAXX];
		memset(log_txt, 0, sizeof(log_txt));
		strcpy(log_txt[0], log_arr[1]);
		strcpy(log_txt[1], log_arr[2]);
		char log_dt[MAXX];
		strcpy(log_dt, log_arr[1]);
		char log_name[MAXX];
		strcpy(log_name, log_arr[2]);
		if (strcmp(log_dt, del_dt) == 0 &&
		    strcmp(log_name, name) == 0 &&
		    index == del_index) {
			del_res = True;
			strcpy(del_txt, log_arr[3]);
			if (__DEBUG) {
				printf("Debug: find same name and index and time need to delete: %s\n", log_arr[0]);
			}
		} else {
			char txt[MAXX];
			memset(txt, 0, sizeof(txt));
			strcat(txt, log_txt[0]);
			strcat(txt, "; ");
			strcat(txt, log_txt[1]);
			strcat(txt, "; ");
			/*strcat(txt,log_txt[2]);
			strcat(txt,"; ");*/
			char msg[MAXX];
			sprintf(msg, "%d; %s", index, txt);
			index++;
			append_new_log_list_for_del_msg_log(msg);
		}
	}
	fclose(msg_file);
	msg_file = fopen("messagelog.txt", "w");
	writelines_new_log_list_for_del_msg_log(msg_file);
	fclose(msg_file);
	del_res_del_txt tmp;
	tmp.del_res = del_res;
	strcpy(tmp.del_txt, del_txt);
	return tmp;
}

char new_log_list_for_show_user_log_exclude_self[MAXX][MAXX];
int num_new_log_list_for_show_user_log_exclude_self = -1;

void append_new_log_list_for_show_user_log_exclude_self(char msg[]) {
	strcpy(new_log_list_for_show_user_log_exclude_self[++num_new_log_list_for_show_user_log_exclude_self], msg);
}

char **show_user_log_exclude_self(char name[]) {
	FILE *user_file = fopen("cse_userlog.txt", "r");
	char log[MAXX];
	while (fgets(log, MAXX, user_file) != NULL) {
		String log_arr[MAXX];
		int num_log_arr = 0;
		split(log, "; ", log_arr, &num_log_arr);
		char log_txt[MAXX][MAXX];
		//TODO:log_txt = log_arr[1:]
		
		char log_name[MAXX];
		strcpy(log_name, log_arr[2]);
		if (strcmp(log_name, name) == 0) {
			if (__DEBUG) {
				printf("Debug: find same name no need to show: %s\n", log_arr[0]);
			}
		} else {
			char txt[MAXX];
			strcat(txt, log_txt[0]);
			strcat(txt, "; ");
			strcat(txt, log_txt[1]);
			strcat(txt, "; ");
			strcat(txt, log_txt[2]);
			char msg[MAXX];
			strcpy(msg, txt);
			append_new_log_list_for_show_user_log_exclude_self(msg);
		}
	}
	return (char **) new_log_list_for_show_user_log_exclude_self;
}

char new_log_list_for_del_user_log[MAXX][MAXX];
int num_new_log_list_for_del_user_log = -1;

void append_new_log_list_for_del_user_log(char msg[]) {
	strcpy(new_log_list_for_del_user_log[++num_new_log_list_for_del_user_log], msg);
}

void writelines_new_log_list_for_del_user_log(FILE *pFile) {
	for (int i = 0; i < num_new_log_list_for_del_user_log; ++i) {
		fputs(new_log_list_for_del_user_log[i], pFile);
	}
}

void del_user_log(char name[1000]) {
	int index = 1;
	FILE *user_file = fopen("cse_userlog.txt", "r");
	char log[MAXX];
	while (fgets(log, MAXX, user_file) != NULL) {
		String log_arr[MAXX];
		int num_log_arr;
		split(log, "; ", log_arr, &num_log_arr);
		char log_txt[MAXX][MAXX];
		//TODO log_txt = log_arr[1:]
		char log_prot[MAXX];
		strcpy(log_prot, log_arr[4]);
		char log_addr[MAXX];
		strcpy(log_addr, log_arr[3]);
		char log_name[MAXX];
		strcpy(log_name, log_arr[2]);
		if (strcmp(log_name, name) == 0) {
			if (__DEBUG) {
				printf("Debug: find same IP need to delete: %s\n", log_arr[0]);
			}
		} else {
			char txt[MAXX];
			memset(txt, 0, sizeof(txt));
			strcat(txt, log_txt[1]);
			strcat(txt, "; ");
			strcat(txt, log_txt[2]);
			strcat(txt, "; ");
			strcat(txt, log_txt[3]);
			strcat(txt, "; ");
			strcat(txt, log_txt[4]);
			char msg[MAXX];
			sprintf(msg, "%d; %s", index, txt);
			index++;
			append_new_log_list_for_del_user_log(msg);
		}
	}
	user_file = fopen("cse_userlog.txt", "w");
	writelines_new_log_list_for_del_user_log(user_file);
	fclose(user_file);
}

void response_to_client(int clientSocket, struct sockaddr_in IP, char recv_msg[]) {
	if (__DEBUG) {
		printf("Debug: recv_msg is %s\n", recv_msg);
	}
	bool socket_status = True;
	char send_msg[MAXX];
	memset(send_msg, 0, sizeof(send_msg));
	String recv_msg_list[MAXX] = {0};
	int len_recv_msg_list = 0;
	split(recv_msg, "%%", recv_msg_list, &len_recv_msg_list);
	bool pwd_status = True;
	char flag[MAXX];
	char FLAG[MAXX];
	char DATA[MAXX];
	if (len_recv_msg_list != 0) {
		memset(flag, 0, sizeof(flag));
		strcpy(flag, recv_msg_list[0]);
	} else {
		puts("[error] parse error");
	}
	
	if (__DEBUG) {
		printf("Debug: flag is %s\n", flag);
	}
	bool is_succeeded_get_client_from_connect_list = False;
	ClientInfo client = get_client_from_connect_list(IP, &is_succeeded_get_client_from_connect_list);
	// response to login
	if (strcmp(flag, "LOGIN") == 0) {
		char name[MAXX];
		char pwd[MAXX];
		int UDPPort;
		strcpy(name, recv_msg_list[1]);
		strcpy(pwd, recv_msg_list[2]);
		sscanf(recv_msg_list[3], "%d", &UDPPort);
//		strcpy(UDPPort, recv_msg_list[3]);
		pwd_status = check_credential(name, pwd);
		// if wrong name and password, number of consecutive failed attempts adds 1
		if (!pwd_status) {
			ClientInfo_error_password(&client);
		}
		
		if (is_succeeded_get_client_from_connect_list == False) {
			ClientInfo_Constructor(&client, IP);
			append_client_connect_list(client);
		}
		// save user information
		bool add_status = ClientInfo_add_logInfo(&client, name, time(0), UDPPort);
		char TYPE[MAXX];
		if (add_status == True) {
			if (pwd_status == False) {
				strcpy(TYPE, "1");
				if (client.m_errorCount == MAX_ERR_COUNT) {
					strcpy(DATA, "Invalid Password. Your account has been blocked. Please try again later");
				} else {
					strcpy(DATA, "Invalid Password. Please try again!");
				}
			} else if (pwd_status == True) {
				strcpy(TYPE, "0");
				strcpy(DATA, "Log in successfully!");
				add_user_log(name, client.m_loginTimestamp, IP, client.m_UDPServerPort);
				char server_output[MAXX];
				sprintf(server_output, "%s log in sucessfully", client.m_username);
				puts(server_output);
			}
		} else {
			strcpy(TYPE, "2");
			strcpy(DATA, "Your account is blocked due to multiple login failures. Please try again later");
		}
		strcpy(FLAG, flag);
		sprintf(send_msg, "%s%%%%%s%%%%%s", FLAG, TYPE, DATA);
	}
		// response to "MSG" command
	else if (strcmp(flag, "MSG") == 0) {
		char txt[MAXX];
		strcpy(txt, recv_msg_list[1]);
		if (__DEBUG) {
			printf("Debug: %s\n", txt);
		}
		int timestamp = time(0);
		int msg_index = add_msg_log(client.m_username, timestamp, txt);
		int dt = timestamp;
		strcpy(FLAG, flag);
		sprintf(DATA, "Message #%d posted at %d", msg_index, dt);
		sprintf(send_msg, "%s%%%%%s", FLAG, DATA);
		char server_output[MAXX];
		sprintf(server_output, "%s posted MSG #%d \"%s\" at %d.", client.m_username, msg_index, txt, dt);
		puts(server_output);
	}
		// response to "RDM" command
	else if (strcmp(flag, "RDM") == 0) {
		strcpy(FLAG, flag);
		memset(DATA, 0, sizeof(DATA));
		char dt[MAXX];
		strcpy(dt, recv_msg_list[1]);
		int timestamp = atoi(dt);
		
		
		char (*log_list)[MAXX] = (char (*)[MAXX]) read_msg_log(timestamp);
		if (num_new_log_list + 1 == 0) {
			strcpy(DATA, "no new message");
		} else {
			for (int i = 0; i < num_new_log_list; ++i) {
				char log[MAXX];
				strcpy(log, log_list[i]);
				String log_arr[MAXX];
				int num_log_arr = 0;
				split(log, "; ", log_arr, &num_log_arr);
				char log_edit_status[MAXX];
				strcpy(log_edit_status, log_arr[4]);
				char log_index[MAXX];
				strcpy(log_index, log_arr[0]);
				char log_name[MAXX];
				strcpy(log_name, log_arr[2]);
				char log_txt[MAXX];
				strcpy(log_txt, log_arr[3]);
				char log_dt[MAXX];
				strcpy(log_dt, log_arr[1]);
				strip(log_edit_status);
				if (strcmp(log_edit_status, "no") == 0) {
					strcpy(log_edit_status, "posted");
				} else if (strcmp(log_edit_status, "yes") == 0) {
					strcpy(log_edit_status, "edited");
				}
				char m[MAXX];
				memset(m, 0, sizeof(m));
				memset(DATA, 0, sizeof(DATA));
				sprintf(m, "#%s %s, %s, %s at %s%%%%", log_index, log_name, log_txt, log_edit_status, log_dt);
				strcat(DATA, m);
				if (__DEBUG) {
					printf("Debug: DATA=%s\n", DATA);
				}
			}
		}
		sprintf(send_msg, "%s%%%%%s", FLAG, DATA);
		// Yoda issued RDM command
		char server_output[MAXX];
		sprintf(server_output, "%s issued RDM command.", client.m_username);
		puts(server_output);
	}
		// response to "EDT" command
	else if (strcmp(flag, "EDT") == 0) {
		strcpy(FLAG, flag);
		int edit_index = atoi(recv_msg_list[1]);
		char edit_dt[MAXX];
		strcpy(edit_dt, recv_msg_list[2]);
		char edit_txt[MAXX];
		strcpy(edit_txt, recv_msg_list[3]);
		edit_res_edit_time tmp = edit_msg_log(client.m_username, edit_index, edit_dt, edit_txt);
		bool edit_res = tmp.edit_res;
		char edit_time[MAXX];
		strcpy(edit_time, tmp.edit_time);
		char TYPE[MAXX];
		char server_output[MAXX];
		if (edit_res) {
			strcpy(TYPE, "0");
			sprintf(DATA, "Message #%d edited at %s.", edit_index, edit_time);
			
			sprintf(server_output, "%s edited MSG #%d \"%s\" at %s.", client.m_username, edit_index, edit_txt,
			        edit_time);
			puts(server_output);
		} else {
			strcpy(TYPE, "1");
			strcpy(DATA, "Edit message fail, can not find such message");
			sprintf(server_output, "%s %s", client.m_username, DATA);
			puts(server_output);
		}
		sprintf(send_msg, "%s%%%%%s%%%%%s", FLAG, TYPE, DATA);
	}
		// response to "DLT" command
	else if (strcmp(flag, "DLT") == 0) {
		strcpy(FLAG, flag);
		int del_index = atoi(recv_msg_list[1]);
		char del_dt[MAXX];
		strcpy(del_dt, recv_msg_list[2]);
		del_res_del_txt tmp = del_msg_log(client.m_username, del_index, del_dt);
		bool del_res = tmp.del_res;
		char del_txt[MAXX];
		strcpy(del_txt, tmp.del_txt);
		char TYPE[MAXX];
		int del_time = time(0);
		char server_output[MAXX];
		if (del_res) {
			strcpy(TYPE, "0");
			sprintf(DATA, "Message #%d deleted at %d.", del_index, del_time);
			sprintf(server_output, "%s deleted MSG #%d \"%s\" at %d.", client.m_username, del_index, del_txt, del_time);
			puts(server_output);
		} else {
			strcpy(TYPE, "1");
			strcpy(DATA, "Delete message fail, can not find such message");
			sprintf(server_output, "%s %s", client.m_username, DATA);
			puts(server_output);
		}
		sprintf(send_msg, "%s%%%%%s%%%%%s", FLAG, TYPE, DATA);
	}
		// response to "ATU" command
	else if (strcmp(flag, "ATU") == 0) {
		strcpy(FLAG, flag);
		memset(DATA, 0, sizeof(DATA));
		char **user_list = show_user_log_exclude_self(client.m_username);
		int NUM = num_new_log_list_for_show_user_log_exclude_self;
		if (NUM + 1 == 0) {
			strcpy(DATA, "no other active user");
		} else {
			for (int i = 0; i < NUM; ++i) {
				char user[MAXX];
				strcpy(user, user_list[i]);
				String user_arr[MAXX];
				int num_user_arr = 0;
				split(user, "; ", user_arr, &num_user_arr);
				char user_dt[MAXX];
				strcpy(user_dt, user_arr[0]);
				char user_name[MAXX];
				strcpy(user_name, user_arr[1]);
				char user_address[MAXX];
				strcpy(user_address, user_arr[2]);
				char user_port[MAXX];
				strcpy(user_port, user_arr[3]);
				char txt[MAXX];
				strip(user_port);
				sprintf(txt, "%s, %s, %s, active since %s.", user_name, user_address, user_port, user_dt);
				strcat(DATA, txt);
				strcat(DATA, "%%");
			}
		}
		sprintf(send_msg, "%s%%%%%d%%%%%s", FLAG, NUM, DATA);
		char server_output[MAXX];
		sprintf(server_output, "%s issued ATU command.", client.m_username);
		puts(server_output);
	}
		// response to "OUT" command
	else if (strcmp(flag, "OUT") == 0) {
		del_user_log(client.m_username);
		client = get_client_from_connect_list(IP, &is_succeeded_get_client_from_connect_list);
		strcpy(FLAG, flag);
		sprintf(send_msg, "%s%%%%Bye, %s!", FLAG, client.m_username);
		socket_status = False;
		char server_output[MAXX];
		sprintf(server_output, "%s logout", client.m_username);
		puts(server_output);
	}
	if (strcmp(send_msg, "") != 0) {
		if (__DEBUG) {
			printf("send_msg: %s\n", send_msg);
		}
		send(clientSocket, send_msg, MAXX, 0);
	}
	if (!socket_status) {
		close(clientSocket);
	}
}

void read_client(char *content, int size, int socket, struct sockaddr_in IP) {
	if (recv(socket, content, size, 0) == -1) {
		char IPdotdec[20]; //存放点分十进制IP地址
		inet_ntop(AF_INET, (void *) &IP, IPdotdec, 16);
		printf("(%s,%d) Left\n", IPdotdec, IP.sin_port);
	}
}

typedef struct _clientSocket_clientAddress {
	int clientSocket;
	struct sockaddr_in clientAddress;
} clientSocket_clientAddress;

void *recv_handler(void *args) {
	clientSocket_clientAddress tmp = *((clientSocket_clientAddress *) args);
	int socket = tmp.clientSocket;
	struct sockaddr_in clientAddress = tmp.clientAddress;
	while (True) {
		String content;
		memset(content, 0, sizeof(content));
		read_client(content, MAXX, socket, clientAddress);
		if (strlen(content) == 0) {
			break;
		} else {
			if (__DEBUG) {
				char IPdotdec[20]; //存放点分十进制IP地址
				inet_ntop(AF_INET, (void *) &clientAddress, IPdotdec, 16);
				printf("(%s,%d) received: %s\n", IPdotdec, clientAddress.sin_port, content);
			}
			response_to_client(socket, clientAddress, content);
		}
	}
}

void start(int server_port) {
	int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in hint;
	hint.sin_family = AF_INET;
	hint.sin_port = htons(server_port);
	inet_pton(AF_INET, "0.0.0.0", &hint.sin_addr);//IP address to number
	bind(serverSocket, (struct sockaddr *) &hint, sizeof(hint));
	listen(serverSocket, 1);
	puts("Server start ... ");
	puts("waiting client ...");
	
	pthread_t th[MAXX];
	int num_thread = 0;
	while (True) {
		struct sockaddr_in client_sockaddr_in;
		socklen_t clientSize = sizeof(client_sockaddr_in);
		
		int clientSocket = accept(serverSocket, (struct sockaddr *) &client_sockaddr_in, &clientSize);
		
		// record each client when server accept successfully
		ClientInfo client;
		ClientInfo_Constructor(&client, client_sockaddr_in);
		append_client_connect_list(client);
		if (__DEBUG) {
			char IPdotdec[20]; //存放点分十进制IP地址
			inet_ntop(AF_INET, (void *) &client_sockaddr_in, IPdotdec, 16);
			printf("(%s,%d)  is trying to connect!\n", IPdotdec, client_sockaddr_in.sin_port);
		}
		
		// start recv_handler for each client
		
		clientSocket_clientAddress tmp = {clientSocket, client_sockaddr_in};
		pthread_create(&th[num_thread], NULL, recv_handler, &tmp);
//		pthread_join(th[num_thread], NULL);
		num_thread++;
	}
}

int main(int argc, char *argv[]) {
	printf("Hello world!\n");
	
	// create messagelog.txt
	FILE *msg_file = fopen("messagelog.txt", "w");
	fclose(msg_file);
	
	// create cse_userlog.txt
	FILE *user_file = fopen("cse_userlog.txt", "w");
	fclose(user_file);
	
	// ./server server_port number_of_consecutive_failed_attempts
	if (argc > 2) {
		int server_port = atoi(argv[1]);
		MAX_ERR_COUNT = atoi(argv[2]);
		start(server_port);
	} else {
		printf("[error] Usage: ./server server_port number_of_consecutive_failed_attempts \n");
	}
}
